//////////////////////////////////////////////////////////////////////////////////
//	 Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>
#include "schedulerctrl.h"
#include <wx/dcbuffer.h>
#include <wx/pen.h> 
#include <wx/rawbmp.h>

#define HEADER_SPACE	12
#define LABEL_SPACE		30
#define BLOCK_W			18
#define BLOCK_H			18
#define FOOTER_SPACE	2*BLOCK_H
#define RIGHT_SPACER	10
#define CLR_ON			wxColor(51,204,51)
#define CLR_OFF			wxColor(51,51,51)
#define CLR_GRAY		wxColor(127,127,127)

//template<class PixelFormat> void GrayOut(wxBitmap& bmp );

CSchedulerCtrl::CSchedulerCtrl(wxWindow* parent, wxWindowID winid, 
							   std::vector<bool>&	arrSched):
	wxPanel(parent, winid), m_arrSched(arrSched)
{
	m_bClick = false;
	m_x = m_y = 0;
	m_stLastIDX = -1;

	m_cEnable = CLR_ON;

	SetBackgroundStyle(wxBG_STYLE_CUSTOM);

	Connect(wxEVT_PAINT,	wxPaintEventHandler(CSchedulerCtrl::OnPaint));
	Connect(wxEVT_SIZE,		wxSizeEventHandler(CSchedulerCtrl::OnSize));
	Connect(wxEVT_LEFT_DOWN,wxMouseEventHandler(CSchedulerCtrl::OnClick));
	Connect(wxEVT_MOTION,	wxMouseEventHandler(CSchedulerCtrl::OnMove));
	
	SetMinSize(wxSize(LABEL_SPACE+BLOCK_W*24+RIGHT_SPACER,
		HEADER_SPACE+BLOCK_H*7+FOOTER_SPACE));
}

CSchedulerCtrl::~CSchedulerCtrl(void)
{
}

bool CSchedulerCtrl::Enable (bool enable)
{
	bool bCur = IsEnabled();
	bool bRes = wxPanel::Enable(enable);

	if ( bCur!=enable)
		Refresh();

	return bRes;
}

/////////////////////////////////////////////////////////////////////////////////
void CSchedulerCtrl::OnSize(wxSizeEvent& event)
{
	Refresh();
}

void CSchedulerCtrl::OnMove(wxMouseEvent& event)
{
	m_bClick = event.ButtonIsDown(wxMOUSE_BTN_LEFT);

	m_x = event.m_x;
	m_y = event.m_y;

	Refresh();

	//m_bClick = false;
}

void CSchedulerCtrl::OnClick(wxMouseEvent& event)
{
	m_stLastIDX = -1;
	m_bClick = true;
	m_x = event.m_x;
	m_y = event.m_y;

	Refresh();
	//m_bClick = false;
}

void CSchedulerCtrl::OnPaint(wxPaintEvent& event)
{
	wxSize szMe = GetClientSize();

	wxBitmap bmp(szMe.x, szMe.y);
	wxBufferedPaintDC dc(this, bmp);

	wxBrush brBack(GetBackgroundColour());

	//Clear
	dc.SetBackground(brBack);
	dc.Clear();

	wxRect rectSchedArea(szMe);

	rectSchedArea.x+=LABEL_SPACE;
	rectSchedArea.y+=HEADER_SPACE;
	rectSchedArea.width  = BLOCK_W*24;
	rectSchedArea.height = BLOCK_H*7;

	//Draw BOX
	//wxBrush br(*wxGREEN);
	//dc.SetBrush(br);

	wxPen pen(*wxBLACK,1,wxSOLID);
	dc.SetPen(pen);

	dc.DrawRectangle(rectSchedArea);

	if ( !IsEnabled() )
	{
		dc.SetTextForeground( CLR_GRAY );
		m_cON  = CLR_GRAY;
		m_cOFF = CLR_GRAY;
	}
	else
	{
		dc.SetTextForeground( *wxBLACK );
		m_cON  = m_cEnable;
		m_cOFF = CLR_OFF;
	}
	
	DrawScheduler( dc, rectSchedArea );
	//DrawGrid( dc, rectSchedArea );
	DrawLabels( dc );
	DrawTimeLine( dc );
	DrawLegend( dc );

	/*
	if ( !IsEnabled() )
	{
		if ( bmp.GetDepth() == wxNativePixelFormat::BitsPerPixel  )
		{
			GrayOut<wxNativePixelFormat>(bmp);
		}
		else 
		if ( bmp.GetDepth() == wxAlphaPixelFormat::BitsPerPixel  )
		{
			GrayOut<wxAlphaPixelFormat>(bmp);
		}

		dc.DrawBitmap( bmp, 0, 0 );
	}*/
}

/*
template<class PixelFormat> void GrayOut(wxBitmap& bmp )
{
	typedef wxPixelData<wxBitmap, PixelFormat> PixelData;

	PixelData data(bmp);

	if ( data )
	{
		

		PixelData::Iterator p(data);

		p.Offset(data, 0, 0);

		for ( int i = 0; i < bmp.GetHeight(); ++i )
		{
			PixelData::Iterator rowStart = p;

			for ( int x = 0; x <bmp.GetWidth(); ++x, ++p )
			{
				//p.Red()=0;
				//p.Green()=0;
				//p.Blue()=0;

				p.Data()=0;

				int nRed = p.Red();
				int nGreen =p.Green();
				int nBl =p.Blue(); 
			}

			p = rowStart;
			p.OffsetY(data, 1);
		}
	}
}*/

bool CSchedulerCtrl::IsMouseInside(int xMouse, int yMouse, int x, int y)
{
	bool bRes(false);

	wxRect rc(x,y, BLOCK_W, BLOCK_H);

	bRes = rc.Contains(xMouse, yMouse);


	return bRes;
}

void CSchedulerCtrl::DrawScheduler( class wxBufferedPaintDC& dc,
								    const wxRect& rectSchedArea )
{
	wxBrush brOn(m_cON);
	wxBrush brOf(m_cOFF);
	wxBrush brOnSel( wxColor(m_cON.Red()+50,m_cON.Green()+50,m_cON.Blue()+50) );
	wxBrush brOfSel( wxColor(m_cOFF.Red()+50,m_cOFF.Green()+50,m_cOFF.Blue()+50) );
	bool bSelected;

	for (size_t i=0;i<7;i++)
	for (size_t j=0;j<24;j++)
	{
		size_t stIDX = i*24+j;
		bSelected = false;

		bool bVal = m_arrSched[stIDX];

		int x = LABEL_SPACE+j*BLOCK_W; 
		int y = HEADER_SPACE+i*BLOCK_H;

		if ( IsMouseInside(m_x, m_y, x, y) )
		{
			DrawBlockLabel(dc, i, j);

			if ( m_bClick &&
				 m_stLastIDX != stIDX )
			{
				m_bClick = false;
				m_x=m_y=0;
				bVal =!bVal;
				m_arrSched[i*24+j] = bVal;
				m_stLastIDX = stIDX;
			}
			else
			{
				bSelected = true;
			}
		}

		if ( bVal )
		{
			dc.SetBrush(bSelected?brOnSel:brOn);
		}
		else
		{
			dc.SetBrush(bSelected?brOfSel:brOf);
		}

		dc.DrawRectangle(x,y, BLOCK_W, BLOCK_H);
	}
}

void CSchedulerCtrl::DrawGrid( class wxBufferedPaintDC& dc, const wxRect& rectSchedArea )
{
	//int nHorzSize = rectSchedArea.GetWidth()/24;
	//int nVertSize = rectSchedArea.GetHeight()/7;

	for (int i=0;i<24;i++)
	{
		dc.DrawLine( rectSchedArea.x+BLOCK_W*i, rectSchedArea.y,
			 		 rectSchedArea.x+BLOCK_W*i, rectSchedArea.y+rectSchedArea.height );
	}

	for (int i=0;i<7;i++)
	{
		dc.DrawLine( rectSchedArea.x, rectSchedArea.y+BLOCK_H*i,
					rectSchedArea.x+rectSchedArea.width , rectSchedArea.y+BLOCK_H*i );
	}
}

void CSchedulerCtrl::DrawLabels( class wxBufferedPaintDC& dc)
{
	wxFont  font(8, wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD|wxFONTFLAG_ANTIALIASED);
	dc.SetFont(font);

	int wText, hText, j=1;

	for ( int i=wxDateTime::Sun;i<=wxDateTime::Sat;i++, j++)
	{
		wxString strLabel = wxDateTime::GetWeekDayName((wxDateTime::WeekDay)i,  wxDateTime::Name_Abbr );

		dc.GetTextExtent(strLabel, &wText, &hText);

		int yTxt = HEADER_SPACE+j*BLOCK_H-hText/2-BLOCK_H/2;
		int xTxt = 0;

		dc.DrawText( strLabel, xTxt, yTxt );		

	}
}

void CSchedulerCtrl::DrawTimeLine( class wxBufferedPaintDC& dc)
{
	wxFont  font( 8, wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD|wxFONTFLAG_ANTIALIASED);
	dc.SetFont(font);

	int wText, hText;

	for ( int i=0;i<=24;i++)
	{
		wxString strLabel = wxString::Format(wxT("%u"), i );
		dc.GetTextExtent(strLabel, &wText, &hText);

		int yTxt = 0;
		int xTxt = LABEL_SPACE+i*BLOCK_W-wText/2;

		dc.DrawText( strLabel, xTxt, yTxt );		
	}
}

void CSchedulerCtrl::DrawBlockLabel(class wxBufferedPaintDC& dc,
									size_t i, size_t j)
{
	wxFont  font( 8, wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_BOLD|wxFONTFLAG_ANTIALIASED);
	dc.SetFont(font);

	wxString strWeekDay = wxDateTime::GetWeekDayName((wxDateTime::WeekDay)i,  wxDateTime::Name_Full );
	wxString strLabel = wxString::Format(wxT(", %.2u-%.2u:59"), j, j );

	int yTxt = HEADER_SPACE+BLOCK_H*7+5;
	int xTxt = LABEL_SPACE;

	dc.DrawText( strWeekDay+strLabel, xTxt, yTxt );		
}

void CSchedulerCtrl::DrawLegend( class wxBufferedPaintDC& dc )
{
	wxString strLabel;
	wxBrush brOn(m_cEnable);
	wxBrush brOf(m_cOFF);

	int y = HEADER_SPACE+BLOCK_H*7+5;
	int x = LABEL_SPACE+10*BLOCK_W;
	int xTxt, yTxt;

	wxFont  font( 8, wxFONTFAMILY_DEFAULT,wxFONTSTYLE_NORMAL,wxFONTWEIGHT_NORMAL|wxFONTFLAG_ANTIALIASED);
	dc.SetFont(font);

	dc.SetBrush(brOn);
	dc.DrawRectangle(x,y, BLOCK_W, BLOCK_H);
	strLabel=_("Enabled");
	xTxt = x+BLOCK_W+5;
	yTxt = y;
	dc.DrawText( strLabel, xTxt, yTxt );

	x+=5*BLOCK_W;
	dc.SetBrush(brOf);
	dc.DrawRectangle(x,y, BLOCK_W, BLOCK_H);
	strLabel=_("Disabled");
	xTxt = x+BLOCK_W+5;
	yTxt = y;
	dc.DrawText( strLabel, xTxt, yTxt );
}